﻿#pragma once
#include "Syntax/Statement/Statement.hpp"
#include "Syntax/Statement/ElseClause.hpp"
#include "Syntax/Expression/Expression.hpp"
#include "Syntax/Statement/ExpressionStatement.hpp"

#include "Text/SourceText.hpp"
#include "CompilationUnit.hpp"
#include "LexicalScanner.hpp"
#include "DiagnosticBag.hpp"
#include "Token.hpp"

#include <memory>
#include <vector>

class SyntaxParser
{

public:
    SyntaxParser(SourceText &text) : arrayIndex(0), Text(text)
    {
        this->diagnostics = std::make_shared<DiagnosticBag>();
        LexicalScanner scanner(text);
        SharedToken token;
        do
        {
            token = scanner.NextToken();
            if (token->isMatch({SyntaxKind::Whitespace, SyntaxKind::Comments}))
                continue;
            this->allTokens.push_back(token);
        } while (!token->isMatch(SyntaxKind::Eof));

        this->diagnostics->AddRange(scanner.Diagnostics());
    }
    // 诊断信息;
    SharedDiagnosticBag Diagnostics() { return this->diagnostics; }

private:
    SharedToken Peek(const size_t offset)
    {
        size_t currIndex = this->arrayIndex + offset;
        if (currIndex >= this->allTokens.size())
            return this->allTokens[this->allTokens.size() - 1];
        return this->allTokens[currIndex];
    }

    inline SharedToken Current() { return Peek(0); }

    inline SharedToken GetNextToken()
    {
        auto curr = this->Current();
        this->arrayIndex++;
        return curr;
    }

    SharedToken Match(SyntaxKind kind)
    {
        if (this->Current()->isMatch(kind))
            return this->GetNextToken();
        auto current = this->Current();
        this->diagnostics->ReportUnexpectedToken(current->GetTextSpan(), current->GetKind(), kind);
        return std::make_shared<SyntaxToken>(kind, current->GetTextName(), current->GetTextValue(), current->GetPosition());
    }
    int GetUnaryOperatorPrecedence(SyntaxKind kind)
    {
        switch (kind)
        {
        case SyntaxKind::Plus:
        case SyntaxKind::Minus:
        case SyntaxKind::Bang:
        case SyntaxKind::Tilde:
            return 6;

        default:
            return 0;
        }
    }
    int GetBinaryOperatorPrecedence(SyntaxKind kind)
    {
        switch (kind)
        {
        case SyntaxKind::Star:
        case SyntaxKind::Slash:
        case SyntaxKind::Mod:
            return 5;
        case SyntaxKind::Plus:
        case SyntaxKind::Minus:
            return 4;
        case SyntaxKind::Equals:
        case SyntaxKind::BangEquals:
        case SyntaxKind::Less:
        case SyntaxKind::LessOrEquals:
        case SyntaxKind::Greater:
        case SyntaxKind::GreaterOrEquals:
            return 3;
        case SyntaxKind::Ampersand:
        case SyntaxKind::AmpersandAmpersand:
            return 2;
        case SyntaxKind::Pipe:
        case SyntaxKind::Hat:
        case SyntaxKind::PipePipe:
            return 1;
        default:
            return 0;
        }
    }
    SharedExpression ParseExpression();
    SharedExpression ParseAssignmentExpression();
    SharedExpression ParseBinaryExpression(int parentPrecedence = 0);
    SharedExpression ParseTerm();
    SharedExpression ParseFactor();
    SharedExpression ParsePrimaryExpression();

    SharedStatement ParseStatement();
    SharedStatement ParseIfStatement();
    SharedStatement ParseForStatement();
    SharedStatement ParseWhileStatement();
    SharedStatement ParseBlackStatement();
    SharedStatement ParseVariableDeclaration();

    SharedElseClause ParseElseClause();

    SharedExpressionStatement ParseExpressionStatement();

public:
    SharedCompilationUnit ParseCompilationUnit()
    {
        auto statement = this->ParseStatement();
        auto endOfFileToken = this->Match(SyntaxKind::Eof);

        return std::make_shared<CompilationUnitSyntax>(statement, endOfFileToken);
    }

private:
    // ;
    SourceText Text;
    // 所有令牌;
    std::vector<SharedToken> allTokens;
    // 当前令牌;
    size_t arrayIndex;
    // 诊断;
    SharedDiagnosticBag diagnostics;
    // std::vector<std::string> diagnostics;
};